// Warning : If you need to use this file, include Util_utils_types.h
namespace Util
{
    /// Base type : unsigned integer
    /** Util's base types are made to allow simpler
    *   manipulation of numbers, booleans and strings.
    *   They simplify conversions (mostly to string),
    *   allow easy access to math/string functions and
    *   implements new functions that are not present
    *   in the standard libraries.<br>
    *   <b>Note</b> : Most of Util's base types are
    *   slower than C++ base types. The difference is
    *   negligible for small operations, but if you need
    *   to do a lot of calculations (matrices, ...) it
    *   can bloat your performances. Use prebuilt types
    *   in this case.
    *   <b>Note</b> : This class is about 60 times slower
    *   than the wrapped POD (20 times in debug, without
    *   any compiler optimizations).
    */
    template<class T>
    class UintType
    {
    public :

        enum Type
        {
            INTEGER,
            INTEGER_INF,
            INTEGER_NAN
        };

        UintType();

        UintType(const T& uiValue);

        explicit UintType(const Type& mType);

        explicit UintType(const float& fValue);

        explicit UintType(const double& dValue);

        template<class N>
        explicit UintType(const UintType<N>& uiValue)
        {
            mType_ = static_cast<Type>(uiValue.GetType());
            uiValue_ = static_cast<T>(uiValue.Get());
        }

        template<class N>
        explicit UintType(const IntType<N>& iValue)
        {
            if (iValue.GetType() == IntType<N>::INTEGER_NAN)
            {
                mType_ = INTEGER_NAN;
                uiValue_ = 0u;
            }
            else if ( (iValue.GetType() == IntType<N>::INTEGER) && (iValue >= 0u) )
            {
                mType_ = INTEGER;
                uiValue_ = static_cast<uint>(iValue.Get());
            }
            else
            {
                mType_ = INTEGER_INF;
                uiValue_ = 0u;
            }
        }

        template<class N>
        explicit UintType(const FloatType<N>& fValue)
        {
            if (fValue.GetType() == FloatType<N>::FLOAT_NAN)
            {
                mType_ = INTEGER_NAN;
                uiValue_ = 0u;
            }
            else if ( (fValue.GetType() == FloatType<N>::FLOAT) && (fValue >= 0.0f) )
            {
                mType_ = INTEGER;
                uiValue_ = static_cast<T>(round(fValue.Get()));
            }
            else
            {
                mType_ = INTEGER_INF;
                uiValue_ = 0u;
            }
        }

        explicit UintType(const Bool& bValue);

        template<class N>
        explicit UintType(const StrType<N>& sValue)
        {
            typename TypeTraits<T>::SignedType i = StringToInt(sValue);

            if (i < 0)
            {
                mType_ = INTEGER_INF;
                uiValue_ = 0u;
            }
            else
            {
                mType_ = INTEGER;
                uiValue_ = static_cast<T>(i);
            }
        }

        /// Returns a const reference to the uint.
        /** \return A const reference to the uint
        */
        inline const T& Get() const { return uiValue_; }

        /// Returns a reference to the uint.
        /** \return A reference to the uint
        */
        inline T& GetR() { return uiValue_; }

        /// Adjusts this uint's value to be contained into the provided interval.
        /** \param uiMin The minimum value
        *   \param uiMax The maximum value
        */
        void Clamp(const UintType& uiMin, const UintType& uiMax);

        /// Returns the power of two just above the value (or equal).
        /** \return The associated power of two (2^n) (superior of equal)
        */
        UintType GetNearestPowerOfTwo() const;

        /// Returns the type of this uint.
        /** \return The type of this uint (infinite, NaN, ...)
        */
        Type GetType() const;

        /// Checks if this uint is infinite
        /** \return 'true' if this uint is infinite
        */
        Bool IsInfinite() const;

        /// Checks if this uint is contained into the provided range.
        Bool IsInRange(const UintType<T>& uiMin, const UintType<T>& uiMax) const;

        /// Checks if this uint is a Not a Number (NaN)
        /** \return 'true' if this uint is NaN
        */
        Bool IsNaN() const;

        /// Checks if this uint equals zero.
        /** \return 'true' if this uint equals zero
        */
        Bool IsNull() const;

        /// Checks if this uint is a valid number.
        /** \return 'true' if this uint is not infinite and a number
        */
        Bool IsValid() const;

        /// Elevates this uint to a certain power (this^n).
        /** \param uiPower The power...
        */
        void Pow(const UintType& uiPower);

        /// Sets the value of the uint to a random number.
        /** \param uiMin The lower bound (minimum)
        *   \param uiMax The upper bound (maximum)
        */
        void Randomize(const UintType& uiMin = 0u, const UintType& uiMax = 1u);

        /// Sets this uint to infinite.
        void SetInfinite();

        /// Set this uint to Not a Number state.
        void SetNaN();

        IntType<typename TypeTraits<T>::SignedType> operator - () const;

        UintType& operator ++ ();
        UintType operator ++ (int);
        UintType& operator -- ();
        UintType operator -- (int);

        UintType operator + (const UintType& uiValue) const;
        UintType operator - (const UintType& uiValue) const;
        UintType operator * (const UintType& uiValue) const;
        UintType operator / (const UintType& uiValue) const;
        UintType operator % (const UintType& uiValue) const;

        void operator += (const UintType& uiValue);
        void operator -= (const UintType& uiValue);
        void operator *= (const UintType& uiValue);
        void operator /= (const UintType& uiValue);
        void operator %= (const UintType& uiValue);

        UintType& operator << (const UintType& uiValue);

        Bool operator == (const UintType& uiValue) const;
        Bool operator != (const UintType& uiValue) const;
        Bool operator < (const UintType& uiValue) const;
        Bool operator > (const UintType& uiValue) const;
        Bool operator <= (const UintType& uiValue) const;
        Bool operator >= (const UintType& uiValue) const;

        static const UintType NaN;
        static const UintType INF;

        /// Returns the lowest value of the two provided ones.
        /** \param uiLeft  The first value
        *   \param uiRight The second value
        *   \return The lowest value of the two provided ones
        */
        static UintType Min(const UintType& uiLeft, const UintType& uiRight);

        /// Returns the highest value of the two provided ones.
        /** \param uiLeft  The first value
        *   \param uiRight The second value
        *   \return The highest value of the two provided ones
        */
        static UintType Max(const UintType& uiLeft, const UintType& uiRight);

        /// Clamps the provided value into the provided interval.
        /** \param uiValue The value to clamp
        *   \param uiMin   The minimum value
        *   \param uiMax   The maximum value
        *   \return The clamped value
        */
        static UintType Clamp(const UintType& uiValue, const UintType& uiMin, const UintType& uiMax);

        /// Returns a random uint in the provided range.
        /** \param uiMin The lower bound (minimum)
        *   \param uiMax The upper bound (maximum)
        *   \return A random uint in the provided range
        */
        static UintType Random(const UintType& uiMin = 0u, const UintType& uiMax = 1u);

    private :

        T uiValue_;

        Type mType_;
    };

    template<class T>
    UintType<T> operator + (const T& uiLeft, const UintType<T>& uiRight);
    template<class T>
    UintType<T> operator - (const T& uiLeft, const UintType<T>& uiRight);
    template<class T>
    UintType<T> operator * (const T& uiLeft, const UintType<T>& uiRight);
    template<class T>
    UintType<T> operator / (const T& uiLeft, const UintType<T>& uiRight);
    template<class T>
    UintType<T> operator % (const T& uiLeft, const UintType<T>& uiRight);

    template<class T>
    UintType<T> operator + (const typename TypeTraits<T>::SignedType& iLeft, const UintType<T>& uiRight);
    template<class T>
    UintType<T> operator - (const typename TypeTraits<T>::SignedType& iLeft, const UintType<T>& uiRight);
    template<class T>
    UintType<T> operator * (const typename TypeTraits<T>::SignedType& iLeft, const UintType<T>& uiRight);
    template<class T>
    UintType<T> operator / (const typename TypeTraits<T>::SignedType& iLeft, const UintType<T>& uiRight);
    template<class T>
    UintType<T> operator % (const typename TypeTraits<T>::SignedType& iLeft, const UintType<T>& uiRight);

    /** \cond NOT_REMOVE_FROM_DOC
    */
    template<> class TypeTraits<uchar>
    {
    public :
        typedef uchar           Type;
        typedef uchar           BaseType;
        typedef UintType<uchar> UtilType;
        typedef uchar           CharType;
        typedef uchar&          RefType;
        typedef uchar           CRefType;
        typedef uchar*          PointerType;

        typedef uchar IsInteger;
        typedef char  SignedType;

        static inline RefType  GetValue(RefType m)  { return m; }
        static inline CRefType GetValue(CRefType m) { return m; }
    };
    template<> class TypeTraits<ushort>
    {
    public :
        typedef ushort           Type;
        typedef ushort           BaseType;
        typedef UintType<ushort> UtilType;
        typedef ushort&          RefType;
        typedef ushort           CRefType;
        typedef ushort*          PointerType;

        typedef ushort IsInteger;
        typedef short  SignedType;

        static inline RefType  GetValue(RefType m)  { return m; }
        static inline CRefType GetValue(CRefType m) { return m; }
    };
    template<> class TypeTraits<uint>
    {
    public :
        typedef uint           Type;
        typedef uint           BaseType;
        typedef UintType<uint> UtilType;
        typedef uint           CharType;
        typedef uint&          RefType;
        typedef const uint&    CRefType;
        typedef uint*          PointerType;

        typedef uint IsInteger;
        typedef int  SignedType;

        static inline RefType  GetValue(RefType m)  { return m; }
        static inline CRefType GetValue(CRefType m) { return m; }
    };
    template<> class TypeTraits<ulong>
    {
    public :
        typedef ulong           Type;
        typedef ulong           BaseType;
        typedef UintType<ulong> UtilType;
        typedef ulong&          RefType;
        typedef const ulong&    CRefType;
        typedef ulong*          PointerType;

        typedef ulong IsInteger;
        typedef long  SignedType;

        static inline RefType  GetValue(RefType m)  { return m; }
        static inline CRefType GetValue(CRefType m) { return m; }
    };
    template<> class TypeTraits<unsigned long long>
    {
    public :
        typedef unsigned long long           Type;
        typedef unsigned long long           BaseType;
        typedef UintType<unsigned long long> UtilType;
        typedef unsigned long long&          RefType;
        typedef const unsigned long long&    CRefType;
        typedef unsigned long long*          PointerType;

        typedef unsigned long long IsInteger;
        typedef long long          SignedType;

        static inline RefType  GetValue(RefType m)  { return m; }
        static inline CRefType GetValue(CRefType m) { return m; }
    };

    template<class T> class TypeTraits< UintType<T> >
    {
    public :
        typedef UintType<T>        Type;
        typedef T                  BaseType;
        typedef UintType<T>        UtilType;
        typedef UintType<T>&       RefType;
        typedef const UintType<T>& CRefType;
        typedef UintType<T>*       PointerType;

        typedef UintType<T> IsInteger;
        typedef IntType<typename TypeTraits<T>::SignedType> SignedType;

        static inline typename TypeTraits<BaseType>::RefType  GetValue(RefType m)  { return m.Get(); }
        static inline typename TypeTraits<BaseType>::CRefType GetValue(CRefType m) { return m.Get(); }
    };
    /** \endcond
    */
}
